//-----------------------------------------------------------------------
//  This file is based on Microsoft Robotics Developer Studio Code Samples.
// 
//  Portions Copyright (C) Microsoft Corporation.  All rights reserved.
//  Updated service created by Trevor Taylor.
//  This code released under the terms of the 
//  Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html)
//
//  $File: HiTechnicGyroSensorTypes.cs $ $Revision: 1 $
//-----------------------------------------------------------------------

using Microsoft.Dss.Core.Attributes;
using System;
using System.Collections.Generic;
using brick = Microsoft.Robotics.Services.Sample.Lego.Nxt.Brick;
using pxanalogsensor = Microsoft.Robotics.Services.AnalogSensor.Proxy;
using System.ComponentModel;
using System.Xml.Serialization;
using Microsoft.Dss.ServiceModel.Dssp;
using Microsoft.Ccr.Core;
using W3C.Soap;
using dssphttp = Microsoft.Dss.Core.DsspHttp;
using Microsoft.Robotics.Services.Sample.Lego.Nxt.Common;


namespace Microsoft.Robotics.Services.Sample.HiTechnic.GyroSensor
{
    /// <summary>
    /// HiTechnic Gyro Sensor Contract
    /// </summary>
    public sealed class Contract
    {
        /// The Unique Contract Identifier for the HiTechnic Gyro Sensor service
        [DataMember, Description("Identifies the Unique Contract Identifier for the HiTechnic Gyro Sensor service.")]
        public const String Identifier = "http://schemas.microsoft.com/robotics/2009/12/hitechnic/nxt/gyrosensor.html";

        /// <summary>
        /// The Gyro Sensor Device Type
        /// </summary>
        [DataMember, Description("Identifies the Gyro Sensor Device Type.")]
        public const string DeviceModel = "GyroSensor";

        /// <summary>
        /// Default Polling Frequency (ms)
        /// </summary>
        [DataMember, Description("Specifies the default Polling Frequency (ms).")]
        public const int DefaultPollingFrequencyMs = 500;

        /// <summary>
        /// Default Offset
        /// </summary>
        /// <remarks>The documentation says 620, but the sensor should be calibrated.</remarks>
        [DataMember, Description("Specifies the default Offset.")]
        public const int DefaultOffset = 620;

    }

    /// <summary>
    /// HiTechnic Gyro Sensor State
    /// </summary>
    [DataContract, Description("Specifies the HiTechnic Gyro Sensor state.")]
    public class GyroSensorState
    {
        /// <summary>
        /// Is the Sensor connected to a LEGO Brick?
        /// </summary>
        [DataMember(IsRequired = false), Description("Indicates a connection to the LEGO NXT Brick Service.")]
        [Browsable(false)]
        public bool Connected;

        /// <summary>
        /// The name of this Gyro Sensor instance
        /// </summary>
        [DataMember, Description("Specifies a user friendly name for the HiTechnic Gyro Sensor.")]
        public string Name;

        /// <summary>
        /// LEGO NXT Sensor Port
        /// </summary>
        [DataMember, Description("Specifies the LEGO NXT Sensor Port.")]
        public NxtSensorPort SensorPort;

        /// <summary>
        /// Polling Frequency (ms)
        /// </summary>
        [DataMember, Description("Indicates the Polling Frequency in milliseconds (0 = default).")]
        public int PollingFrequencyMs;

        /// <summary>
        /// The Offset for the zero reading
        /// </summary>
        [DataMember, Description("Indicates the Offset for the zero reading.")]
        public int Offset;

        /// <summary>
        /// The current Rotation Speed (deg/sec). Positive is clockwise.
        /// </summary>
        [DataMember, Description("Indicates the current Rotation Speed (deg/sec). Positive is clockwise.")]
        public int RotationSpeed;

        /// <summary>
        /// The time of the last sensor update
        /// </summary>
        [DataMember, Description("Indicates the time of the last sensor reading.")]
        [Browsable(false)]
        public DateTime TimeStamp;
    }

    /// <summary>
    /// Get the Gyro Sensor State
    /// </summary>
    [Description("Gets the current state of the Gyro Sensor.")]
    public class Get : Get<GetRequestType, PortSet<GyroSensorState, Fault>>
    {
    }

    /// <summary>
    /// Configure Device Connection
    /// </summary>
    [DisplayName("ConnectionUpdate")]
    [Description("Connects the Gyro Sensor to be plugged into the NXT Brick.")]
    public class ConnectToBrick : Update<GyroSensorConfig, PortSet<DefaultUpdateResponseType, Fault>>
    {
        /// <summary>
        /// Configure Device Connection
        /// </summary>
        public ConnectToBrick()
        {
        }

        /// <summary>
        /// Configure Device Connection
        /// </summary>
        /// <param name="sensorPort"></param>
        public ConnectToBrick(NxtSensorPort sensorPort)
            : base(new GyroSensorConfig(sensorPort))
        {
        }

    }

    /// <summary>
    /// HiTechnic Gyro Sensor Configuration.
    /// </summary>
    [DataContract, Description("Specifies the HiTechnic Gyro Sensor Configuration.")]
    public class GyroSensorConfig
    {
        /// <summary>
        /// Gyro Sensor Configuration.
        /// </summary>
        public GyroSensorConfig() { }

        /// <summary>
        /// HiTechnic Gyro Sensor Configuration.
        /// </summary>
        /// <param name="sensorPort"></param>
        public GyroSensorConfig(NxtSensorPort sensorPort)
        {
            this.SensorPort = sensorPort;
        }

        /// <summary>
        /// The name of this Gyro Sensor instance
        /// </summary>
        [DataMember, Description("Specifies a user friendly name for the Gyro Sensor.")]
        public string Name;

        /// <summary>
        /// LEGO NXT Sensor Port
        /// </summary>
        [DataMember, Description("Specifies the LEGO NXT Sensor Port.")]
        [DataMemberConstructor]
        public NxtSensorPort SensorPort;

        /// <summary>
        /// Polling Freqency (ms)
        /// </summary>
        [DataMember, Description("Indicates the Polling Frequency in milliseconds (0 = default).")]
        public int PollingFrequencyMs;

        /// <summary>
        /// The offset for the zero reading
        /// </summary>
        [DataMember, Description("Indicates the offset for the zero reading (0 = default).")]
        public int Offset;

    }

    /// <summary>
    /// Replace the Gyro Sensor State
    /// </summary>
    [Description("Replaces the current state of the Gyro Sensor.")]
    public class Replace : Replace<GyroSensorState, PortSet<DefaultReplaceResponseType, Fault>>
    {
    }

    /// <summary>
    /// Subscribe to Gyro Sensor notifications
    /// </summary>
    [Description("Subscribes to Gyro Sensor notifications.")]
    public class Subscribe : Subscribe<SubscribeRequestType, PortSet<SubscribeResponseType, Fault>>
    {
    }

    /// <summary>
    /// Gyro Sensor Operations
    /// </summary>
    [ServicePort]
    public class GyroSensorOperations : PortSet
    {
        /// <summary>
        /// Gyro Sensor Operations
        /// </summary>
        public GyroSensorOperations()
            : base(
                typeof(DsspDefaultLookup),
                typeof(DsspDefaultDrop),
                typeof(ConnectToBrick),
                typeof(dssphttp.HttpGet),
                typeof(Get),
                typeof(Subscribe),
                typeof(Replace)
            )
        { }


        /// <summary>
        /// Add Types with an inherited PortSet
        /// </summary>
        /// <param name="types"></param>
        protected void AddTypes(params Type[] types)
        {
            List<Type> newTypes = null;
            List<IPort> newPortsTable = new List<IPort>();

            if (types != null && types.Length > 0)
            {
                newTypes = new List<Type>(types);
                foreach (Type t in types)
                {
                    Type portType = typeof(Port<>).MakeGenericType(t);
                    newPortsTable.Add((IPort)Activator.CreateInstance(portType));
                }
            }
            else
                newTypes = new List<Type>();

            if (base.Types != null && base.Types.Length > 0)
            {
                int iy = 0;
                foreach (Type t in base.Types)
                {
                    if (ValidateType(t, newTypes))
                    {
                        newTypes.Add(t);
                        newPortsTable.Add(base.PortsTable[iy]);
                    }
                    iy++;
                }
            }

            base.PortsTable = newPortsTable.ToArray();
            base.Types = newTypes.ToArray();
        }

        private bool ValidateType(Type newType, List<Type> types)
        {
            if (types.Contains(newType))
                return false;
            Type requestType = null;
            if (newType.BaseType.IsGenericType)
                requestType = newType.BaseType.GetGenericArguments()[0];

            foreach (Type t in types)
            {
                if (newType.IsInstanceOfType(t))
                    return false;
                if (t.BaseType.IsGenericType && t.BaseType.GetGenericArguments()[0].Equals(requestType))
                    return false;
            }
            return true;
        }

        /// <summary>
        /// Post Configure Sensor Connection with body and return the response port.
        /// </summary>
        public virtual PortSet<DefaultUpdateResponseType, Fault> ConnectToBrick(GyroSensorConfig body)
        {
            ConnectToBrick op = new ConnectToBrick();
            op.Body = body ?? new GyroSensorConfig();
            this.PostUnknownType(op);
            return op.ResponsePort;
        }

        /// <summary>
        /// Post Configure Sensor Connection with body and return the response port.
        /// </summary>
        public virtual PortSet<DefaultUpdateResponseType, Fault> ConnectToBrick(GyroSensorState state)
        {
            ConnectToBrick op = new ConnectToBrick();
            op.Body = new GyroSensorConfig(state.SensorPort);
            op.Body.Name = state.Name;
            op.Body.PollingFrequencyMs = state.PollingFrequencyMs;
            op.Body.Offset = state.Offset;

            this.PostUnknownType(op);
            return op.ResponsePort;
        }

        /// <summary>
        /// Post Configure Sensor Connection with body and return the response port.
        /// </summary>
        /// <param name="sensorPort"></param>
        /// <returns></returns>
        public virtual PortSet<DefaultUpdateResponseType, Fault> ConnectToBrick(NxtSensorPort sensorPort)
        {
            ConnectToBrick op = new ConnectToBrick(sensorPort);
            this.PostUnknownType(op);
            return op.ResponsePort;
        }
    }
}
